﻿// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "FloatCurveWrapper.generated.h"

struct FFloatCurveInitParam;

/**
 * 对浮点曲线 UCurveFloat 的包装类
 */
UCLASS()
class MOTIONSUBTITLES_API UFloatCurveWrapper : public UObject
{
	GENERATED_BODY()

public:
	/// 根据给定的曲线资产的软路径，以及初始化参数，进行初始化
	/// @param InCurveAssetPath 曲线资产的软路径
	/// @param InInitParam 曲线初始化参数
	/// @return 是否成功初始化
	bool Init(const FString& InCurveAssetPath, const FFloatCurveInitParam& InInitParam);

	/// 根据给定的曲线资源的指针，以及初始化参数，进行初始化
	/// @param InCurvePtr 曲线资源的指针
	/// @param InInitParam 曲线初始化参数
	/// @return 是否成功初始化
	bool Init(const UCurveFloat* InCurvePtr, const FFloatCurveInitParam& InInitParam);

	/// 判断是否有效
	bool IsValid() const;

	/// 根据给定的时间，获取曲线的值
	/// @param InTime 时间，单位：秒
	/// @param bForceCheckValid 是否强制检查有效性
	/// @return 曲线的值
	float GetValueByTime(float InTime, bool bForceCheckValid = false) const;

	/// 把曲线的起和止的值，限定在0和1这样的“标准化”性质的数值区间内；
	/// 并根据时间(限定为0到1的范围)，实时获取当前值（已标准化）
	/// @param InTime 时间，范围从0到1
	/// @param bForceCheckValid 是否强制检查有效性
	/// @return 标准化的值
	float GetNormalizedValueByTime(float InTime, bool bForceCheckValid = false) const;

	/// 获取曲线资源（如果需要的话）
	UCurveFloat* GetCurveAsset() const;

	/// 修改曲线的时间范围
	/// @param InCurvePtr 曲线的指针
	/// @param InNewStartTime 指定的新起始时间
	/// @param InNewEndTime 指定的新结束时间
	/// @return 是否修改成功
	bool ChangeTimeRange(UCurveFloat* InCurvePtr, float InNewStartTime, float InNewEndTime);

private:
	/// 把曲线的起和止的值，限定在0和1这样的“标准化”性质的数值区间内；
	/// 注意：根据沟通，上层的需求仅仅是希望起和止的值都在0和1之间，并不要求曲线的中间值都在0和1之间。
	/// 这意味着，也许曲线的中间会出现 -0.2 或 1.2 这样的值。
	/// 所以，此处所做的工作，并非真正意义上的标准化。
	/// @param InOriginalCurvePtr 原始曲线资源的指针
	/// @return 是否成功初始化
	bool InitNormalizedCurve(const UCurveFloat* InOriginalCurvePtr);

	/// 初始化，并且保留原始曲线资产中的所有数据，不做任何修改
	/// @return 是否成功初始化
	bool InitWithoutChange();

	/// 根据给定的最小值、最大值，初始化曲线资源
	/// @param InCurvePtr 曲线的指针
	/// @param InMinValue 最小值
	/// @param InMaxValue 最大值
	/// @return 是否成功初始化
	bool InitByValueRange(UCurveFloat* InCurvePtr, const float InMinValue, const float InMaxValue);

	/// 根据给定的起始值、结束值，初始化曲线资源。
	/// 注意：曲线中间的某个值，可能会比起始值和结束值都大（或都小）
	/// 备注：当曲线的起始值和终止值之间的“升降关系”，与参数中给定的起始值和终止值之间的“升降关系”不一致时，
	/// 会自动翻转曲线的起始值和终止值，使得二者一致。并针对每个关键帧的“值”进行翻转。
	/// @param InCurvePtr 曲线的指针
	/// @param InStartValue 起始值
	/// @param InEndValue 结束值
	/// @return 是否成功初始化
	bool InitByStartEndValues_AutoFlipByValue(UCurveFloat* InCurvePtr, float InStartValue, float InEndValue);

	/// 根据给定的起始值、结束值，初始化曲线资源。
	/// 注意：曲线中间的某个值，可能会比起始值和结束值都大（或都小）
	/// 备注：当曲线的起始值和终止值之间的“升降关系”，与参数中给定的起始值和终止值之间的“升降关系”不一致时，
	/// 本函数会自动翻转曲线的关键帧的时间戳，使得曲线的变化趋势、升降关系与参数中给定的起始值和终止值之间的“升降关系”一致。
	/// @param InCurvePtr 曲线的指针
	/// @param InStartValue 起始值
	/// @param InEndValue 结束值
	/// @return 是否成功初始化
	bool InitByStartEndValues_AutoFlipByTime(UCurveFloat* InCurvePtr, float InStartValue, float InEndValue);

	/// 判断曲线是否有效
	/// @param InCurve 送入的曲线
	/// @return 是否有效
	bool IsCurveValid(UCurveFloat* InCurve) const;

private:
	UPROPERTY()
	TObjectPtr<UCurveFloat> CurveAsset;

	/// 标准化曲线：根据原有曲线的起和止的值进行缩放，限定在0和1这样的标准化的数值区间内；
	UPROPERTY()
	TObjectPtr<UCurveFloat> NormalizedCurve;
};
